package twoD.hofem;

/**
 * A function f: R -> R of the form u(x) = K0 + k1*x + K2*x^2 + ... + Kp*x^p is
 * called a polynomial of degree p.
 * 
 * @author Team 5
 */

public class PolynomialRToR implements FunctionRToR {

	private double a_, b_;
	private Interval domain_;
	private double[] coeffecient;

	/**
	 * Polynomial u is fully characterized by its coefficient vector In the
	 * Constructor: a, b is domain, c are coefficients.
	 * 
	 */
	public PolynomialRToR(double a, double b, double... c) {
		a_ = a;
		b_ = b;
		domain_ = new Interval(a_, b_);
		coeffecient = c;
	}

	/**
	 * Add two polynomials
	 * 
	 * @param p
	 *            polynomial to be added
	 * @return new polynomial
	 */

	public PolynomialRToR add(PolynomialRToR p) {
		int m = coeffecient.length, n = p.coeffecient.length;
		double[] coeff = new double[Math.max(m, n)];
		for (int i = 0; i < Math.min(m, n); i++) {
			coeff[i] = coeffecient[i] + p.coeffecient[i];
		}
		for (int i = Math.min(m, n); i < Math.max(m, n); i++) {
			if (m > n)
				coeff[i] = coeffecient[i];
			else
				coeff[i] = p.coeffecient[i];
		}

		return new PolynomialRToR(a_, b_, coeff);

	}

	/**
	 * Multiply a polynomial by a number alpha
	 * 
	 * @return new polynomial
	 */

	public PolynomialRToR multiply(double alpha) {

		double[] coeff = new double[coeffecient.length];
		for (int i = 0; i < coeffecient.length; i++) {
			coeff[i] = alpha * coeffecient[i];
		}
		return new PolynomialRToR(a_, b_, coeff);
	}

	/**
	 * Multiply two polynomials
	 * 
	 * @return new polynomial
	 */

	public PolynomialRToR multiply(PolynomialRToR p) {
		int m = coeffecient.length, n = p.coeffecient.length;
		double q, r;
		double[] coeff = new double[m + n - 1];
		for (int j = 0; j < coeff.length; j++) {
			for (int i = 0; i <= j; i++) {
				if (i <= (m - 1))
					q = coeffecient[i];
				else
					q = 0;
				if ((j - i) <= (n - 1))
					r = p.coeffecient[j - i];
				else
					r = 0;

				coeff[j] += q * r;
			}
		}
		return new PolynomialRToR(a_, b_, coeff);
	}

	/**
	 * Evaluate the derivative of polynomial at the specified point using
	 * efficient evaluation of polynomials ( Horner's Algorithm )
	 * 
	 * @param x
	 *            point
	 * @return the derivative value
	 */
	public double derivativeAt(double x) {
		double sum = (coeffecient.length - 1)
				* coeffecient[coeffecient.length - 1];
		if (coeffecient.length == 1)
			return sum;
		else {
			for (int i = coeffecient.length - 2; Math.abs(i) > 0; i--) {
				sum = sum * x + i * coeffecient[i];
			}
			return sum;
		}
	}

	public Interval getDomain() {
		return domain_;
	}

	/**
	 * Evaluate the polynomial at the specified point using efficient evaluation
	 * of polynomials ( Horner's Algorithm )
	 * 
	 * @param x
	 *            point
	 * @return the polynomial value
	 */
	public double valueAt(double x) {
		double sum = coeffecient[coeffecient.length - 1];
		for (int i = coeffecient.length - 2; i >= 0; i--) {
			sum = sum * x + coeffecient[i];
		}
		return sum;
	}

	public double[] getBreakpoints() {
		return null;
	}

	public int getP() {
		return (coeffecient.length - 1);
	}

	public double[] getCoefficients() {
		return coeffecient;
	}
}
